package com.uxwine.alarm.alert;

import java.io.IOException;

import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.content.res.Resources;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.RingtoneManager;
import android.media.AudioManager.OnAudioFocusChangeListener;
import android.media.MediaPlayer.OnErrorListener;
import android.net.Uri;
import android.os.Vibrator;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;

import com.nny.alarm.R;
import com.uxwine.alarm.Alarm;
import com.uxwine.util.Log;

/**
 * 本类负责处理震动、铃声等效果。同时，还要监控两个事件：<br/>
 * 1. OnAudioFocusChangeListener<br/>
 * 2. PhoneStateListener<br/>
 * 并将这两个事件通过OnEffectListener接口传递给调用者。以
 * 方便在来电或者其他事件时，调用者进行处理。
 * 
 * @author  zhangzhongming
 * @date	2013.5.2
 *
 */
public class AlarmEffect
{
	/** Play alarm up to 10 minutes before silencing */
    private static final long[] VIBRATE_PATTERN = new long[] { 500, 500 };
    
    // Volume suggested by media team for in-call alarms.
    private static final float IN_CALL_VOLUME = 0.125f;

    private boolean mPlaying = false;
    private Vibrator mVibrator;
    private MediaPlayer mMediaPlayer;
    private TelephonyManager mTelephonyManager;
    private int mInitialCallState;
    private AudioManager mAudioManager = null;
    private Context mCtx;
    private OnEffectListener mOnEffectListener;
      
    public AlarmEffect(final Context ctx)
    {
    	mCtx = ctx;  
    	mVibrator = (Vibrator)mCtx.getSystemService(Context.VIBRATOR_SERVICE);
    	mAudioManager = (AudioManager)mCtx.getSystemService(Context.AUDIO_SERVICE); 
    	mTelephonyManager = (TelephonyManager)mCtx.getSystemService(Context.TELEPHONY_SERVICE);
    	mPlaying = false;
    }
    
    public void init(OnEffectListener l)
    {
    	setOnEffectListener(l);
    	mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
    }
    
    public void uninit()
    {
    	// Stop listening for incoming calls.
    	mTelephonyManager.listen(mPhoneStateListener, 0);
    	mAudioManager.abandonAudioFocus(mAudioFocusListener);
    	mTelephonyManager = null;
    	mAudioManager = null;
    	mVibrator = null;
    }
    
    public void setOnEffectListener(OnEffectListener l)
    {
    	mOnEffectListener = l;
    }
    
    public boolean isPlaying()
    {
    	return mPlaying;
    }
    
    public boolean isLoss()
    {
    	return (!mPlaying && mMediaPlayer != null);
    }
    
    public void pause()
    {
    	if(null != mMediaPlayer)
    		mMediaPlayer.pause();
    }
    
    public void play(Alarm alarm)
    {
    	
        mAudioManager.requestAudioFocus(
        		mAudioFocusListener, 
        		AudioManager.STREAM_ALARM,
                AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
        
        // stop() checks to see if we are already playing.
        stop();

        if (!alarm.silent ) 
        {  
            // TODO: Reuse mMediaPlayer instead of creating a new one and/or use
            // RingtoneManager.
            mMediaPlayer = new MediaPlayer();
            mMediaPlayer.setOnErrorListener(new OnErrorListener() 
            {
                public boolean onError(MediaPlayer mp, int what, int extra) 
                {
                    Log.d("zhangzhongming", "Error occurred while playing audio.");
                    mp.stop();
                    mp.release();
                    mMediaPlayer = null;
                    return true;
                }
            });

            try 
            {
                // Check if we are in a call. If we are, use the in-call alarm
                // resource at a low volume to not disrupt the call.
                if (mTelephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE)
                {
                    Log.d("zhangzhongming", "Using the in-call alarm");
                    mMediaPlayer.setVolume(IN_CALL_VOLUME, IN_CALL_VOLUME);
                    setDataSourceFromResource(mCtx.getResources(), mMediaPlayer,
                            R.raw.in_call_alarm);
                } 
                else 
                {
                	 // Fall back on the default alarm if the database does not have an
                    // alarm stored.
                    Uri alert = alarm.alert; 
                    if (alert == null)
                    {
                        alert = RingManager.getSystemRing();
                        Log.d("zhangzhongming", "Using default alarm: " + alert.toString());
                        mMediaPlayer.setDataSource(mCtx, alert);
                    }
                    else
                    {
                    	int type = RingManager.getRingType(alert);
                    	if(type == RingManager.TYPE_SYSTEM)
                    		setDataSourceByRingUri(mCtx, mMediaPlayer, alert);
                    	else if(type == RingManager.TYPE_LOCAL)
                    		mMediaPlayer.setDataSource(mCtx, alert);
                    }
                }
                startAlarm(mMediaPlayer);
                
            }
            catch (Exception ex) 
            {
                Log.d("zhangzhongming", "Using the fallback ringtone");
                // The alert may be on the sd card which could be busy right
                // now. Use the fallback ringtone.
                try 
                {
                    // Must reset the media player to clear the error state.
                    mMediaPlayer.reset();
                    setDataSourceFromResource(mCtx.getResources(), mMediaPlayer,
                            R.raw.fallbackring);
                    startAlarm(mMediaPlayer);
                } 
                catch (Exception ex2)
                {
                    // At this point we just don't play anything.
                    Log.d("zhangzhongming", "Failed to play fallback ringtone"+ex2);
                }
            }
        }

        /* Start the vibrator after everything is ok with the media player */
        if (alarm.vibrate) 
        {
            mVibrator.vibrate(VIBRATE_PATTERN, 0);
        } 
        else 
        {
            mVibrator.cancel();
        }
        // Record the initial call state here so that the new alarm has the
        // newest state.
        mInitialCallState = mTelephonyManager.getCallState();
        mPlaying = true;
    }
    
    public void stop()
    {
    	if(mPlaying)
    	{
    		mPlaying = false;
    		
    		if(mMediaPlayer != null)
    		{
    			mMediaPlayer.stop();
    			mMediaPlayer.release();
    			mMediaPlayer = null;
    		}
    		mVibrator.cancel();
    	}
    }
    
 // Do the common stuff when starting the alarm.
    private void startAlarm(MediaPlayer player)
            throws java.io.IOException, IllegalArgumentException,
                   IllegalStateException 
    {
        // do not play alarms if stream volume is 0
        // (typically because ringer mode is silent).
        if (mAudioManager.getStreamVolume(AudioManager.STREAM_ALARM) != 0) 
        {
            player.setAudioStreamType(AudioManager.STREAM_ALARM);
            player.setLooping(true);
            player.prepare();
            player.start();
        }
    }

    private void setDataSourceFromResource(Resources resources,
            MediaPlayer player, int res) throws java.io.IOException 
    {
        AssetFileDescriptor afd = resources.openRawResourceFd(res);
        if (afd != null) {
            player.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(),
                    afd.getLength());
            afd.close();
        }
    }
    
    public void setDataSourceByRingUri(Context ctx, MediaPlayer player, Uri uri) throws IOException
    {
    	AssetFileDescriptor afd = RingManager.getRingRaw(ctx, uri);
    	if(afd != null)
    	{
    		player.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(),
                    afd.getLength());
            afd.close();
    	}
    }
    
    
    private OnAudioFocusChangeListener mAudioFocusListener = new OnAudioFocusChangeListener() 
    {
        public void onAudioFocusChange(int focusChange) {
            if(null != mOnEffectListener)
            	mOnEffectListener.onAudioFocusChange(focusChange);
        }
    };
    
    private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
        @Override
        public void onCallStateChanged(int state, String ignored) 
        {
        	// The user might already be in a call when the alarm fires. When
            // we register onCallStateChanged, we get the initial in-call state
            // which kills the alarm. Check against the initial call state so
            // we don't kill the alarm during a call.
            if((null != mOnEffectListener) && 
            		(state != TelephonyManager.CALL_STATE_IDLE) &&
            		(state != mInitialCallState))
            {
            	mOnEffectListener.onCallStateChanged(state, ignored);
            }
        }
    };
    
    public static interface OnEffectListener
    {
    	public void onAudioFocusChange(int focusChange);
    	public void onCallStateChanged(int state, String ignored);
    }
}
